package com.kuang.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;

// 如何解决CAS的ABA问题：原子戳引用，带个版本号
// 注意包装类的坑：
// 包装类数据范围导致获取到的值不是预期内存中的值
// Integer使用了对象缓存机制，默认范围-128-127，超过区间外数据会在堆上，不会复用已有对象
public class Test3 {

    // 初始化原子戳引用  初识引用：2022， 初识版本：1
    static AtomicStampedReference<Integer> integerAtomicStampedReference = new AtomicStampedReference<>(1, 1);

    public static void main(String[] args) {
        // 模拟两个线程并发执行，获取版本号，只要有线程对值进行修改，就修改版本号，避免ABA问题
        new Thread(()->{
            int stamp = integerAtomicStampedReference.getStamp(); // 先获取版本号，睡5秒
            System.out.println("a1=>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2); // 等2s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程a要把1改成6
            System.out.println(Thread.currentThread().getName()+"-1=>6->"+integerAtomicStampedReference.compareAndSet(
                    1,
                    6,
                    integerAtomicStampedReference.getStamp(),
                    integerAtomicStampedReference.getStamp() + 1));//获取是否达到期望修改
            System.out.println("a2=>" + integerAtomicStampedReference.getStamp());
            // 再把6改成1
            System.out.println(Thread.currentThread().getName()+"-6=>1->"+integerAtomicStampedReference.compareAndSet(
                    6,
                    1,
                    integerAtomicStampedReference.getStamp(),
                    integerAtomicStampedReference.getStamp() + 1));
            System.out.println("a3=>" + integerAtomicStampedReference.getStamp());
        },"a").start();

        new Thread(()->{
            int stamp = integerAtomicStampedReference.getStamp(); // 先获取版本号，睡5秒
            System.out.println("b1=>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(5);// 等5s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 好人线程要把1改成6，改不了，因为预期版本可能不是1，是已经被A改过的2
            System.out.println(Thread.currentThread().getName()+"-1=>6-"+integerAtomicStampedReference.compareAndSet(
                    1,
                    6,
                    stamp,
                    stamp + 1));
            System.out.println("b2=>" + integerAtomicStampedReference.getStamp());
        },"b").start();


        /*
            a1=>1
            b1=>1
            a-1=>6->true
            a2=>2
            a-6=>1->true
            a3=>3
            b-1=>6-false  b线程等5s,所以a会先修改完，这时候，版本为3，b修改时，与预期版本1不一致，就不修改
            b2=>3
         */
    }
}
